<!DOCTYPE HTML>
<html><head>
<title>Symbol inspector - Woboq Code Browser</title>
<script type="text/javascript" src="./jquery/jquery.min.js"></script>
<script type="text/javascript" src="./jquery/jquery-ui.min.js"></script>

<link rel="stylesheet" href="kdevelop.css">
<style>/*<![CDATA[*/

body { color:#000000; background-color:#ffffff; padding:1em; margin: 0;  font-family: Helvetica,sans-serif; }
a { color: #037; border: none; text-decoration: none }
a:hover{  text-decoration: underline }
#footer { font-size: smaller; margin:1ex; color: #333; text-align: right }
#footer img { vertical-align: middle; }
img { border: none; }
input#searchline { margin: 1ex;  width: 30em; max-width: 50%; }
p {  margin:0; padding: 0}
h3 { padding: 0; margin: 1em 0 0.5ex 0; }
.code i { white-space: pre-wrap; font-family: monospace }
body > h1 { float:right; }

ul { margin:0; padding-left: 1em }
/*h1 { float: right; font-weight: large; padding: 0px; margin:1px;
background-image: url(woboq-48.png); background-repeat: no-repeat;
height:48px; width: 126px; overflow:hidden
}
h1 a { visibility:hidden }*/

a { text-decoration:none}
a.opener { color:inherit }

hr { margin: 0; border-collapse:collapse;  }

table#tree { margin: 0; padding: 1ex 1ex; }
table#tree { width:640px; text-align:left;
border-left: 1px solid #D8D8D8; border-right: 1px solid #D8D8D8;
margin: auto}


table#tree table { padding:0; padding-left: 4ex; width:100%; margin:0; border:0; }
table#tree td { white-space: pre; font-family:monospace; margin:0; padding:0; border: 0 }
table#tree tr { margin:0; border:0; padding:0 }

/*]]>*/</style>


<script>//<![CDATA[
"use strict";

$(function(){
//BEGIN duplicated from codebrowser.js

window.ecma_script_api_version = 2;

// ATTENTION: Keep in sync with C++ function of the same name in filesystem.cpp and `Generator::escapeAttrForFilename`
var replace_invalid_filename_chars = function (str) {
    if(window.ecma_script_api_version && window.ecma_script_api_version >= 2) {
        return str.replace(new RegExp(':', 'g'), '.');
    }

    return str;
}

var escape_selector = function (str) {
    return str.replace(/([ #;&,.+*~\':"!^$[\]()=<>|\/@{}\\])/g,'\\$1')
}

function escape_html(str) {
    return $("<p/>").text(str).html();
}

var useExplain = {
    r: "r: The variable is read",
    w: "w: The variable is modified",
    a: "a: The address is taken",
    c: "c: The function is called",
    m: "m: a member is accessed",
    "?": "?: The type of use of the variable is unknown"
};


// demangle the function name, don't care about the template or the argument
function demangleFunctionName(mangle) {
    if (! mangle) return mangle;
    if (mangle[0] !== '_') return mangle;
    if (mangle[1] !== 'Z') return mangle;
    mangle = mangle.slice(2);
    var result;
    var last = "";
    var scoped = false;
    do {
        if (!result)
            result = "";
        else
            result += "::";
        if (mangle[0]==='D') {
            result += "~" + last;
            break;
        }
        if (mangle[0]==='C') {
            result += last;
            break;
        }
        if (mangle[0]==='N') {
            mangle = mangle.slice(1);
            scoped = true;
        }
        if (mangle[0]==='K') mangle = mangle.slice(1);
        if (mangle[0]==='L') mangle = mangle.slice(1);
        if (mangle.match(/^St/)) { //St
            mangle = mangle.slice(2);
            result += "std::";
        }
        if (mangle[0]==='I') {
            var n = 1;
            var i;
            for (i = 1; i < mangle.length && n > 0 ;i++) {
                if (mangle[i] === 'I') n++;
                if (mangle[i] === 'E') n--;
            }
            mangle = mangle.slice(i);
        }
        if (mangle.match(/^[a-z]/)) {
            result += "operator";
            break;
        }
        var len = parseInt(mangle);
        if (!len) return null;
        var start = ("" + len).length;
        last = mangle.substr(start, len);
        result += last;
        mangle = mangle.slice(start + len)
    } while(mangle && mangle[0]!='E' && mangle[0]!='B' && scoped);
    return result;
}

//### [...]

var prefixLen = function( s1 , s2) {
    var maxMatchLen = Math.min(s1.length, s2.length);
    var res = -1;
    while (++res < maxMatchLen) {
        if (s1.charAt(res) != s2.charAt(res))
            break;
    }
    return res * 256 + 256 - s1.length;
}

//END

function getParameterByName(name) {
    var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
    return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}

    var root_path = getParameterByName("root");

    if(root_path.substr(-1) === '/') { // remove trailing slash
        root_path = root_path.substr(0, root_path.length - 1);
    }
    var proj_root_path = root_path;
    var ref = getParameterByName("ref");
    var file = "";

    if (!ref) {
        $("#main").html("<h2>Error</h2><p>Invalid symbol</p>");
        return;
    }

    var url = proj_root_path + "/refs/" + replace_invalid_filename_chars(ref);

    $.get(url, function(data) {
        var type ="", content ="";
        var res = $("<data>"+data+"</data>");

        content += "<h2>" + escape_html(demangleFunctionName(ref)) + "</h2>";

        var types = res.find("[type]");
        if (types.length) {
            content += "<h3>Type</h3>";
            types.each(function() {
                var t = $(this).attr("type");
                content += "<p class='code'><span class='type'>" + escape_html(t) + "</span></p>";
            });
        }



//BEGIN extracted from the part of computeTooltipContent from codebrowser.js

        var typePrefixLen = -1;

        //comments:
        var seen_comments = [];
        res.find("doc").each(function() {
            var comment = $(this).html();
            if ($.inArray(comment, seen_comments) !== -1)
                return;

            if (seen_comments.length == 0) { // ###
                content += "<h3>Documentation comments</h3><p class='code'>"
            }
            seen_comments.push(comment);
            /* ###
            if (comment.length > 550) {
                // FIXME: we should not split in an escape code
                comment = comment.substr(0, 500) + "<a href='#' class='expandcomment'> [more...]</a><span style='display:none'>" + comment.substr(500) + "</span>";
            }
            */
            content += "<i>" + comment + "</i>"; //###

            var f = $(this).attr("f");
            var l = $(this).attr("l");
            if (f && l) {
                var url = proj_root_path + "/" + f + ".html#" + l;
                content += " <a href='" + url +"'>&#8618;</a>";
            }
            content += "<br />"; //###
        });

        if (seen_comments.length) content += "</p>"; //###


        var p = function (label, tag) {
            var d = res.find(tag);
            if (!d.length)
                return;
//            content += "<br/>" + label + ": (" + d.length + ")"; //###
            content += "<h3>" + label + ": (" + d.length + ")</h3><p>"; //###
            var shouldCompress = d.length > 15;
            var dict = { number: 0 };
            d.each(function() {
                var th = $(this);
                var f = th.attr("f");
                var l = th.attr("l");
                var t = th.attr("type");
                if (t) {
                    var prefixL = prefixLen(f, file)
                    if (prefixL >= typePrefixLen) {
                        typePrefixLen = prefixL;
                        type = t;
                    }
                }
                /* ###
                if (shouldCompress) {
                    if (!Object.prototype.hasOwnProperty.call(dict, f)) {
                        dict[f] = [];
                        dict.number++;
                    }
                    dict[f].push(l);
                } else*/ {
                    var url = proj_root_path + "/" + f + ".html#" + l;
                    content += "<a href='" + url +"' >" + f + ":" + l + "</a>"; //###
                    if (tag === "ovr" || tag === "inh") {
                        var c = th.attr("c");
                        if (c)
                            content += " (" + demangleFunctionName(c) + ")";
                    }
                    content += "<br />"; //###

                }
            });
            /* ###
            if (shouldCompress) {
                if (dict.number > 40) {
                    content += "<br/>(Too many)";
                    return;
                }
                for(var f in dict) {
                    if (!Object.prototype.hasOwnProperty.call(dict,f) || f==="number") continue;
                    var url_begin = proj_root_path + "/" + f + ".html";
                    content += "<br/><a href='" + url_begin + "#" + dict[f][0] +"' >" + f +  "</a>";
                    var len = dict[f].length;
                    if (len > 100 || (f !== file && len >= 5))
                        content += " (" + len + " times)";
                    else {
                        content += ": <a href='" + url_begin + "#" + dict[f][0] +"' >" + dict[f][0] +"</a>";
                        for(var i = 1; i < len; i++) {
                            content += ", <a href='" + url_begin + "#" + dict[f][i] +"' >" + dict[f][i] +"</a>";
                        }
                    }
                }
            }
            */
        }
        p("Definitions", "def");
        p("Declarations", "dec");
        //var isType = elem.hasClass("type");
        var isType = false; //### FIXME
        p(isType ? "Inherit" : "Overrides", "inh");
        p(isType ? "Inherited by" : "Overriden by", "ovr");

        // Size:
        var size = res.find("size");
        if (size.length === 1) {
            content += "<br/>Size: " + escape_html(size.text()) + " bytes";
        }
        var offset = res.find("offset");
        if (offset.length === 1) {
            content += "<br/>Offset: " + escape_html(offset.text() >> 3) + " bytes";
        }

        // Uses:
        var uses = res.find("use");
        /* ###
        if (uses.length) {
            content += "<br/><a href='#' class='showuse'>Show Uses:</a> (" + uses.length + ")<br/><span class='uses_placeholder'></span>"
        }

        var useShown = false;
        showUseFunc = function(e) {
            if (useShown) {
                tt.find(".uses").toggle();
                return false;
            }
            */
            var dict = { };
            var usesTypeCount = { };
            uses.each(function() {
                var t = $(this);
                var f = t.attr("f");
                var l = t.attr("l");
                var c = t.attr("c");
                var u = t.attr("u");
                //if (!u) u = "?"
                var url = proj_root_path + "/" + f + ".html#" + l;
                if (!Object.prototype.hasOwnProperty.call(dict, f)) {
                    dict[f] = { elem: $("<li/>").append($("<a/>").attr("href", url).text(f)),
                                contexts: {},  prefixL: prefixLen(file, f), count: 0,
                                f: f, brk: t.attr("brk")
                    };
                }
                c = demangleFunctionName(c)
                if (!c) c = f + ":" + l;
                dict[f].count++;
                usesTypeCount[u||"?"] = (usesTypeCount[u||"?"]||0) + 1;

                if (!Object.prototype.hasOwnProperty.call(dict[f].contexts, c)) {
                    dict[f].contexts[c] = $("<li/>").append($("<a/>").attr("href", url).text(c));
                    dict[f].contexts[c].count = 1;
                    if (u) {
                        dict[f].contexts[c].usesType = "<abbr title='"+ useExplain[u] +"'>"+u+"</abbr>";
                        dict[f].contexts[c].usesRaw = u;
                    } else {
                        dict[f].contexts[c].usesType = "";
                        dict[f].contexts[c].usesRaw = "?";
                    }
                } else {
                    dict[f].contexts[c].count++;
                    if (dict[f].contexts[c].usesRaw.indexOf(u||"?") === -1) {
                        if (u)
                            dict[f].contexts[c].usesType += "<abbr title='"+ useExplain[u] +"'>"+u+"</abbr>";
                        dict[f].contexts[c].usesRaw += (u||"?");
                    }
                }
            });
            var list = [];
            for (var xx in dict) {
                if (Object.prototype.hasOwnProperty.call(dict, xx))
                    list.push(dict[xx]);
            }
            // ###
            //list.sort(function(a,b){ var dif = b.prefixL - a.prefixL; return dif ? dif : a.brk ? 1 : b.f - a.f });
            var ul = $("<ul class='uses'/>");
            for (var i = 0; i < list.length; ++i) {
                var usestypes = "";
                var subul = $("<ul/>");
                for (var xx in list[i].contexts) if (Object.prototype.hasOwnProperty.call(list[i].contexts, xx)) {
                    var context = list[i].contexts[xx];
                    usestypes += context.usesRaw;
                    subul.append(list[i].contexts[xx].append(" (" + context.count+" " + context.usesType + ")")
                        .attr("data-uses", context.usesRaw));
                }
                ul.append(list[i].elem.append(" (" + list[i].count+")").attr("data-uses",usestypes).append(subul));
            }
//END
        if (uses.length > 0) {
            content += "<h3 id='uses'>Uses (" + uses.length + ")</h3>" ;
            content += "<form>";
            var useLabel = {
                r: "Read",
                w: "Write",
                a: "Address Taken",
                c: "Called",
                m: "Member Accessed",
                "?": "Others"
            };
            for (var xx in usesTypeCount) {
                if (useLabel[xx]) {
                    content += "<label title='" + useExplain[xx]
                        + "'><input type='checkbox' checked='1' data-check='" + xx + "' />"
                        + useLabel[xx] + " (" + usesTypeCount[xx] + ") &nbsp; </label> &nbsp; ";
                }
            }
            content += "</form>" + $("<span>").append(ul).html();
        }

        $("#main").html(content);
        $("#uses+form input").change(function() {
            var style = "[data-uses]"
            $("#uses+form input:checked").each(function() {
                style+=":not([data-uses*='"+$(this).attr("data-check")+"'])";
            });
            style+="{display:none}"
            var s = $("style#uses_style");
            if (s.length) s.text(style);
            else $("<style id='uses_style'>"+style+"</style>").appendTo("head");
        });

        buildInheritenceGraph(ref, res)

        // Jump to anchor:
        if (location.hash)
            window.location.href = location.hash

    }).fail(function() {
        $("#main").html("<h2>Error</h2><p>Symbol not found</p>");
    });

//----------------- SEARCH LINE  (everything duplicated)

    var searchline = $("input#searchline");
    var searchTerms;
    searchline.focus(function() {
        if (searchTerms)
            return;
        searchTerms = {}
        var fileIndex = [];
        var functionDict = {};

        // Do a google seatch of the text on the project.
        var text_search = function(text) {
            /* ###
            var location = "" + (window.location);
            var idx = location.indexOf(file);
            if (idx < 0)
                return;
            location = location.substring(0, idx);
            window.location = "http://google.com/search?sitesearch=" + encodeURIComponent(location) + "&q=" + encodeURIComponent(text);
            */
        }

//BEGIN  code duplicated in indexscript.js
        // callback for jqueryui's autocomple activate
        var activate = function(event,ui) {
            var val = ui.item.value;
            var type = searchTerms[val] && searchTerms[val].type;
            if (type == "file") {
                window.location = root_path + '/' +  searchTerms[val].file + ".html";
            } else if (type == "ref") {
                var ref = searchTerms[val].ref;
                var url = root_path + "/refs/" + replace_invalid_filename_chars(ref);
                $.get(url, function(data) {
                    var res = $("<data>"+data+"</data>");
                    var def =  res.find("def");
                    var result = {  len: -1 };
                    def.each( function() {
                        var cur = { len : -1,
                                    f : $(this).attr("f"),
                                    l : $(this).attr("l") }

                        cur.len = prefixLen(cur.f, file)
                        if (cur.len >= result.len) {
                            result = cur;
                            result.isMarcro = ($(this).attr("macro"));
                        }
                    });

                    if (result.len >= 0) {
                        var newloc = root_path + "/" + result.f + ".html#" +
                            (result.isMarcro ? result.l : ref );
                        window.location = newloc;
                    }
                });
            } else {
                text_search(val);
            }
        };

        var getFnNameKey = function (request) {
            if (request.indexOf('/') != -1 || request.indexOf('.') != -1)
                return false;
            var mx = request.match(/::([^:]{2})[^:]*$/);
            if (mx)
                return mx[1].toLowerCase().replace(/[^a-z]/, '_');
            request = request.replace(/^:*/, "");
            if (request.length < 2)
                return false;
            var k = request.substr(0, 2).toLowerCase();
            return k.replace(/[^a-z]/, '_')
        }

        var autocomplete = function(request, response) {
            var term = $.ui.autocomplete.escapeRegex(request.term);
            var rx1 = new RegExp(term, 'i');
            var rx2 = new RegExp("(^|::)"+term.replace(/^:*/, ''), 'i');
            var functionList = [];
            var k = getFnNameKey(request.term)
            if (k && Object.prototype.hasOwnProperty.call(functionDict,k)) {
                functionList = functionDict[k].filter(
                    function(word) { return word.match(rx2) });
            }
            var l = fileIndex.filter( function(word) { return word.match(rx1); });
            l = l.concat(functionList);
            l = l.slice(0,1000); // too big lists are too slow
            response(l);
        };

        searchline.autocomplete( {source: autocomplete, select: activate, minLength: 4  } );

        searchline.keypress(function(e) {
            var value = searchline.val();
            if(e.which == 13) {
                activate({}, { item: { value: value } });
            }
        });

        // When the content changes, fetch the list of function that starts with ...
        searchline.on('input', function() {
            var value = $(this).val();
            var k = getFnNameKey(value);
            if (k && !Object.prototype.hasOwnProperty.call(functionDict, k)) {
                functionDict[k] = []
                $.get(root_path + '/fnSearch/' + k, function(data) {
                    var list = data.split("\n");
                    for (var i = 0; i < list.length; ++i) {
                        var sep = list[i].indexOf('|');
                        var ref = list[i].slice(0, sep);
                        var name = list[i].slice(sep+1);
                        searchTerms[name] = { type:"ref", ref: ref };
                        functionDict[k].push(name);
                    }
                    if (searchline.is(":focus")) {
                        searchline.autocomplete("search", searchline.val());
                    }
                });
            }
        });

        // Pasting should show the autocompletion
        searchline.on("paste", function() { setTimeout(function() {
                searchline.autocomplete("search", searchline.val());
            }, 0);
        });
//END

        // Fetch the list of all files
        $.get(root_path + '/fileIndex', function(data) {
            var list = data.split("\n");
            fileIndex = list;
            for (var i = 0; i < list.length; ++i) {
                searchTerms[list[i]] = { type:"file", file: list[i] };
            }
            if (searchline.is(":focus")) {
                searchline.autocomplete("search", searchline.val());
            }
        });

        return false;
    });

//------------------------------------------------------------------------------------------------
// Graph

    // Math.sign is ES06,  this is a naive implementation that works for our use case
    if (!Math.sign) Math.sign = function(a) { return a < 0 ? -1 : a > 0 ? 1 : 0 };

    var graph = {
        config: { boxPadding: 5, rowSpacing:50, boxSpacing:15, arrowSide:10,  textColor: "black" },
        nodes: {},
        edges: [],
        addNode: function(ref, data)  {
            if (!Object.prototype.hasOwnProperty.call(this.nodes, ref)) {
                this.nodes[ref] = { ref:ref, label:ref, downLink: [], upLink: [], height:0  };
            }
            var node = this.nodes[ref];
            if (data) for (var xx in data) {
                if (Object.prototype.hasOwnProperty.call(data, xx))
                    node[xx] = data[xx];
            }
            return node;
        },
        addEdge: function(a, b) {
            var na = this.addNode(a);
            var nb = this.addNode(b);
            na.downLink.push(b);
            nb.upLink.push(a);
        },
        render: function(centralNode) {
            if (!Object.prototype.hasOwnProperty.call(this.nodes, centralNode))
                return;
            var cn = this.nodes[centralNode];

            var svgns = "http://www.w3.org/2000/svg";
            var svg = $("<svg width='100%' height='390'/>")
            $("#graph").html(svg);
            $("#graph").prepend("<h3>Inheritance diagram</h3>");


            // step 1: compute the sizes
            for (var xx in this.nodes) {
                if (!Object.prototype.hasOwnProperty.call(this.nodes, xx))
                    continue;
                var n = this.nodes[xx];
                n.textNode = document.createElementNS(svgns, "text");
                n.textNode.setAttributeNS(null, "fill", this.config.textColor);
                n.textNode.textContent = n.label;
                svg.append(n.textNode);
                var bb = n.textNode.getBBox();
                n.textWidth = bb.width;
                n.textHeight = bb.height;
            }

            // step 2: generate rows:
            var g = this;
            var rows={};
            var maxHeight = 0;
            var minHeight = 0;
            function computeRows(up, node, height) {
                var array = up ? node.upLink : node.downLink;
                if (!array)
                    return;
                maxHeight = Math.max(maxHeight, height);
                minHeight = Math.min(minHeight, height);
                if (!rows[height])
                    rows[height] = [];
                array.forEach(function(ref) {
                    var n = g.nodes[ref];
                    if (Math.abs(height) > Math.abs(n.height) )
                        n.height = height;
                    rows[height].push(ref);
                    computeRows(up, n, up?height+1:height-1);
                });
            }
            rows[0]=[centralNode];
            computeRows(true, cn, 1);
            computeRows(false, cn, -1);

            // step 3: compute height and width
            function sum(array) { var s = 0; array.forEach(function(x){s+=x;}); return s; }
            var w_sum = 0;
            var w_max = 0;
            var h_sum = 0;
            var h_max = 0;
            for (var r = minHeight; r <= maxHeight; ++r) {
                if (!rows[r].length) continue;
                var ws = rows[r].map(function(x){ return g.nodes[x].textWidth + 2 * g.config.boxPadding; });
                var hs = rows[r].map(function(x){ return g.nodes[x].textHeight + 2 * g.config.boxPadding; });
                w_max = Math.max(w_max, sum(ws) + rows[r].length*g.config.boxSpacing);
                h_max = Math.max(h_max, sum(hs) + rows[r].length*g.config.boxSpacing);
                w_sum += g.config.rowSpacing + Math.max.apply(Math, ws);
                h_sum += g.config.rowSpacing + Math.max.apply(Math, hs);
            }

            // we take the layout that has the smaller width
            var horizontal = (w_max > w_sum);
            var w = horizontal ? w_sum : w_max;
            var h = horizontal ? h_max : h_sum;
            svg.attr("width", w);
            svg.attr("height", h);


            //step 4: layout items;
            var y = 0;
            var x = 0;
            var space = 0;
            for (var r = minHeight; r <= maxHeight; ++r) {
                if (!rows[r].length) continue;
                var ws = rows[r].map(function(x){ return g.nodes[x].textWidth + 2 * g.config.boxPadding; });
                var hs = rows[r].map(function(x){ return g.nodes[x].textHeight + 2 * g.config.boxPadding; });
                if (horizontal) {
                    space = (h - sum(hs)) / rows[r].length;
                    y = space/2;
                } else {
                    space = (w - sum(ws)) / rows[r].length;
                    x = space/2;
                }
                for (var i = 0; i < rows[r].length; ++i) {
                    var n = g.nodes[rows[r][i]];
                    if (n.height !== r || n.drawnAlready)
                        continue;
                    n.drawnAlready = true;
                    n.textNode.setAttributeNS(null, "x", x + g.config.boxPadding);
                    n.textNode.setAttributeNS(null, "y", y + g.config.boxPadding + n.textHeight*2/3);
                    n.x = x;
                    n.y = y;

                    var box = document.createElementNS(svgns, "rect");
                    box.setAttributeNS(null, "fill", "transparent");
                    box.setAttributeNS(null, "stroke", r === 0 ? "red": "black");
                    box.setAttributeNS(null, "x", x);
                    box.setAttributeNS(null, "y", y);
                    box.setAttributeNS(null, "width", n.textWidth + 2 * g.config.boxPadding);
                    box.setAttributeNS(null, "height", n.textHeight + 2 * g.config.boxPadding);

                    if (n.link) {
                        var link = document.createElementNS(svgns, "a");
                        link.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", n.link);
                        link.appendChild(box);
                        svg.append(link);
                    } else {
                        svg.append(box);
                    }

                    if (horizontal)
                        y += space + n.textHeight;
                    else
                        x += space + n.textWidth;

                    // draw the arrow
                    n.downLink.forEach(function(l) {
                        var n2 = g.nodes[l];
                        var x1, x2, y1, y2;
                        if (horizontal) {
                            x1 = n.x;
                            x2 = n2.x + g.config.boxPadding*2 + n2.textWidth;
                            y1 = n.y + g.config.boxPadding + n.textHeight/2;
                            y2 = n2.y + g.config.boxPadding + n2.textHeight/2
                        } else {
                            x1 = n.x + g.config.boxPadding + n.textWidth/2;
                            x2 = n2.x + g.config.boxPadding + n2.textWidth/2;
                            y1 = n.y;
                            y2 = n2.y + g.config.boxPadding*2 + n2.textHeight
                        }
                        var line = document.createElementNS(svgns, "line");
                        line.setAttributeNS(null, "x1", x1);
                        line.setAttributeNS(null, "x2", x2);
                        line.setAttributeNS(null, "y1", y1);
                        line.setAttributeNS(null, "y2", y2);
                        line.setAttributeNS(null, "stroke", "black")
                        svg.append(line)

                        var arrow_side=g.config.arrowSide;
                        var arrow = document.createElementNS(svgns, "polygon");
                        arrow.setAttributeNS(null, "fill", "blue");

                        var arrow_x = x2;
                        var arrow_y = y2;

                        if (Math.abs(x1-x2) < 1) {
                            arrow.setAttributeNS(null, "points",
                                            arrow_x+","+arrow_y+" "+
                                            (arrow_x+arrow_side/3)+","+(arrow_y+arrow_side)+" "+
                                            (arrow_x-arrow_side/3)+","+(arrow_y+arrow_side));
                        } else if(Math.abs(y1-y2) < 1) {
                            arrow.setAttributeNS(null, "points",
                                            arrow_x+","+arrow_y+" "+
                                            (arrow_x+arrow_side)+","+(arrow_y+arrow_side/3)+" "+
                                            (arrow_x+arrow_side)+","+(arrow_y-arrow_side/3));
                        } else {
                            var a = (y1-y2)/(x1-x2);
                            var px = (arrow_side/Math.sqrt(1+a*a)) * Math.sign(x1-x2);

                            var py = px*a;

                            var ppx = (arrow_side/3)/Math.sqrt(1+1/(a*a));
                            var ppy = -ppx/a;

                            arrow.setAttributeNS(null, "points",
                                    arrow_x+","+arrow_y+" "+
                                    (arrow_x+px+ppx)+","+(arrow_y+py+ppy)+" "+
                                    (arrow_x+px-ppx)+","+(arrow_y+py-ppy));
                        }

                        svg.append(arrow);

                    });
                }
                if (horizontal)
                    x += g.config.rowSpacing + Math.max.apply(Math, ws);
                else
                    y += g.config.rowSpacing + Math.max.apply(Math, hs);

            }
        }
    }

    var classData = { };

    var waiting = 0;
    function maybeDraw() {
        waiting--;
        if (!waiting) {
            updateGraph();
        }
    }

    function expandGraphRec(ref, up, data, max_depth) {
        if (up) classData[ref] = data;
        var d = data.find(!up ? "ovr" : "inh");
        if (d.length >= 30)
            return; // Graph too big
        if (d.length >= 1) {
            graph.addNode(ref, { label: demangleFunctionName(ref), fetched: true });
        }
        var changed = false;
        waiting++;
        d.each(function() {
            var th = $(this);
            var c = th.attr("c");
            if (c) {
                //var l = proj_root_path + "/" + escape_html(th.attr("f")) + ".html#" + escape_html(c);
                var l = "symbol.html?root=" + escape_html(proj_root_path) + "&ref=" + escape_html(c);
                var n = graph.addNode(c, { label: demangleFunctionName(c), link: l });
                if (up)
                    graph.addEdge(ref, c);
                else
                    graph.addEdge(c, ref);
                changed = true;
                if (max_depth <= 0 || n.fetched)
                    return;
                var url = proj_root_path + "/refs/" + replace_invalid_filename_chars(c);
                waiting++;
                n.fetched = true;
                $.get(url, function(data) {
                    var res = $("<data>"+data+"</data>");
                    expandGraphRec(c, up, res, max_depth -1);
                    maybeDraw();
                }).fail(maybeDraw);
            }
        });
        maybeDraw();
    }

    function buildInheritenceGraph(ref, data) {
        expandGraphRec(ref, true, data, 10);
        expandGraphRec(ref, false, data, 7);
    }

    function updateGraph() {
        graph.render(ref);
        buildLayout(ref);
        // Jump to anchor:
        if (location.hash)
            window.location.href = location.hash
    }

//------------------------------------------------------------------------------------------------
// Class layout


    function buildLayout(ref) {
        var html = "";
        // Note, sometimes there is a virtual table pointer before the first base.
        // There is also sometimes alignement between bases which are not taken into account
        function buildLayoutRecursive(data, baseOffset, depth) {
            if (depth > 255) return;
            // Go in every bases
            var baseOffset2 = baseOffset;
            data.find("inh").each(function() {
                var c = $(this).attr("c");
                if (c && classData[c]) {
                    var baseData = classData[c];
                    var baseSize = parseInt(baseData.find("size").text());
                    buildLayoutRecursive(baseData, baseOffset2, depth + 1);
                    if (baseSize)
                        baseOffset2 += baseSize;
                }
            });
            data.find("mbr").each(function() {
                var th = $(this);
                var o = parseInt(th.attr('o'))/8 + baseOffset;
                o = o || "";
                var r = th.attr('r');
                var t = th.attr('t');
                html += "<tr data-ref='"+escape_html(r)+"'><th>" + o + "</th><td>"
                    + escape_html(t) + "</td><td><a href='#'>"
                    + escape_html(demangleFunctionName(r)) + "</a></td></tr>";
            });
        }
        if (classData[ref])
            buildLayoutRecursive(classData[ref], 0, 0);

        if (html) {
            $("#layout").html("<h3>Class layout</h3><table border='1'>"
                +"<tr><th>Offset</th><th>Type</th><th>Member</th></tr>"+html+"</table>");
            var getUrl = function(ref, callback) {
                var url = proj_root_path + "/refs/" + replace_invalid_filename_chars(ref);
                $.get(url, function(data) {
                    var res = $("<data>"+data+"</data>");
                    var def =  res.find("dec[f],def[f]");
                    if (def.length > 0) {
                        var def = $(def[0]);
                        var file = def.attr("f");
                        var line = def.attr("l");
                        var isMarcro = def.attr("macro");
                        //var brk = def.attr("brk"); //--
                        callback(proj_root_path + "/" + file + ".html#" + (isMarcro ? result.line : ref ));
                    }
                });
            };

            $('#layout [data-ref]').on(
                { "click": function(e) {
                    var tt = $(this);
                    if (tt.attr("href") === "#") {
                        getUrl(tt.parent().parent().attr("data-ref"), function(url) {
                            window.location = url;
                        });
                        e.preventDefault()
                        return false;
                    }
                }, "mouseenter": function(e) {
                    var tt = $(this);
                    if (tt.attr("href") === "#" && !this._requested) {
                        this._requested = true;
                        getUrl(tt.parent().parent().attr("data-ref"), function(url) {
                            tt.attr("href", url);
                        });
                    }
                }}, "a");
        }
    }
//------------------------------------------------------------------------------------------------


});

//]]>
</script>

</head>

<body>

<h1><a href="http://code.woboq.org"><img src='woboq-48.png' alt="Woboq"/></a></h1>
<input id='searchline' type='text' placeholder='Search a file or function'/>


<div id="main">
<p>Loading...</p>
</div>

<div id="graph"></div>
<div id="layout"></div>
</body>
</html>

